#!/usr/bin/env python
#
# capouik.py - a simple pcap fuzzer using mangle.py from fusil and pcs to parse pcap and inject new packets.
#
# Very bad coding approuved.
#
# (c) 2oo9, Clement LECIGNE
#
import sys
import socket
import time
import getopt

from array import array
from pcs import PcapConnector, IPPROTO_TCP, IPPROTO_UDP
from pcs.packets.ipv4 import ipv4
from pcs.packets.tcp import tcp
from pcs.packets.udp import udp
from mangle import MangleConfig, Mangle


class Capouik:

    def __init__(self, pcap = None, target = None, seed = 0, occ = 5, waitack = False, pktref = 0):
        self.pcap = pcap
        self.target = target
        self.seed = seed
        self.occ = occ
        self.waitack = waitack
        self.pktref = pktref

    def __ip_source(self, pkt):
        """
        return source ip address found in pkt
        """
        return pkt.data.src

    def __ip_dest(self, pkt):
        """
        return destination ip address found in pkt
        """
        return pkt.data.dst

    def __tcp_port(self, pkt):
        """
        return TCP destination port used in pkt
        """
        return pkt.data.data.dport

    def __proto(self, pkt):
        """
        return proto found in packet
        """
        return pkt.data.protocol

    def __conn(self, port, proto = socket.SOCK_STREAM):
        print "connecting to %s:%s" % (self.target, port)
        self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.conn.connect((self.target, port))

    def __send(self, data):
        self.conn.send(data)

    def __close(self):
        self.conn.close()

    def __isalive(self, port):
        """
        return False if target is dead
        """
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((self.target, port))
            s.close()
        except:
            return False
        return True

    def run(self):
        """
        main
        """
        for i in xrange(0, self.occ):
            fd = PcapConnector(self.pcap)
            mangle_cfg = MangleConfig(seed = self.seed, max_op = 50)
            ipsrc = None
            ipdst = None
            dport = 0
            pktidx = 0
            try:
                while True:
                    pktidx += 1
                    pkt = fd.readpkt()

                    # skip to pktref is specified
                    if self.pktref != 0 and self.pktref < pktidx:
                        continue

                    # read ip source if not specified
                    if not ipsrc:
                        ipsrc = self.__ip_source(pkt)

                    # read ip destination if not specified
                    if not ipdst:
                        ipdst = self.__ip_dest(pkt)

                    # grab target port if not already calculated
                    if dport == 0 and self.__proto(pkt) == IPPROTO_TCP:
                        dport = self.__tcp_port(pkt)
                        self.__conn(dport)

                    # skip not wanted packets
                    if ipsrc != self.__ip_source(pkt) or ipdst != self.__ip_dest(pkt):
                        continue

                    # go fuzzing for TCP
                    if self.__proto(pkt) == IPPROTO_TCP:
                        if pkt.data and pkt.data.data and pkt.data.data.data:
                            data = array('B', pkt.data.data.data.bytes)
                            if data and len(data) > 0:
                                mangle = Mangle(mangle_cfg, data)
                                mangle.run()
                                self.__send(data.tostring())
                                time.sleep(1)
                                if self.__isalive(dport) == False:
                                    print "Seems to be owned with seed = %d" % self.seed
                                    return
                if self.seed > 0:
                    self.seed += 1
            except:
                try:
                    self.__close()
                except:
                    pass
        return

def usage():
    print "usage: %s -p pcap -t target [-o occ] [-s seed] [-r pktref]"

if __name__ == "__main__":
    capouik = Capouik()
    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:s:o:t:r:h", ["pcap=", "seed=", "occ=", "target=", "pktref=", "help"])
    except getopt.GetoptError, err:
        print str(err)
        usage()
        sys.exit(2)
    for o, a in opts:
        if o in ("-p", "--pcap"):
            capouik.pcap = a
        elif o in ("-t", "--target"):
            capouik.target = a
        elif o in ("-s", "--seed"):
            capouik.seed = int(a)
        elif o in ("-o", "--occ"):
            capouik.occ = int(a)
        elif o in ("-r", "--pktref"):
            capouik.pktref = int(a)
        elif o in ("-h", "--help"):
            usage()
            sys.exit()
        else:
            assert False, "unhandled option"

    capouik.run()
